/**
 * 3D模型加载工具类（支持glTF/GLB格式）- 适配微信小程序环境
 */
// 声明小程序环境中的全局API类型，解决TypeScript识别问题
declare global {
  // 新增FrameRequestCallback类型声明
  interface FrameRequestCallback {
    (timestamp: number): void;
  }
  
  var requestAnimationFrame: (callback: FrameRequestCallback) => number;
  var cancelAnimationFrame: (id: number) => void;
  var TextDecoder: {
    new(encoding?: string): {
      decode(buffer: ArrayBuffer): string;
    }
  };
  
  // 修正URL类型声明，确保包含href属性
  interface URL {
    href: string;
  }
  
  var URL: {
    new(url: string): URL;
  };
}

// 扩展微信小程序环境的类型定义
declare interface Wx {
  requestAnimationFrame: (callback: FrameRequestCallback) => number;
  cancelAnimationFrame: (id: number) => void;
  TextDecoder: {
    new(encoding?: string): {
      decode(buffer: ArrayBuffer): string;
    }
  };
  URL: {
    new(url: string): URL;
  };
  createSelectorQuery: () => any;
  showToast: (options: any) => void;
  // 修复：补充getSystemInfoSync返回类型，添加窗口尺寸信息
  getSystemInfoSync: () => { pixelRatio: number, windowWidth: number, windowHeight: number };
  request: (options: any) => void;
  showLoading: (options: any) => void;
  hideLoading: () => void;
  // 修复：添加nextTick方法的类型声明
  nextTick: (callback: (...args: any[]) => void) => void;
}

declare var wx: Wx;

// 添加WebGL类型声明
declare interface WebGLShader {}
declare interface WebGLProgram {}

export class ModelLoader {
  private canvas: any;
  private gl: any;
  private isInitialized: boolean = false;
  private initCallbacks: Array<() => void> = [];
  private animationFrameId: number | null = null;
  private modelData: any = null;
  private shaderProgram: any = null;
  private vertexBuffer: any = null;
  private indexBuffer: any = null;
  private vertexCount: number = 0;
  
  // 模型变换参数
  private scale: number = 1.0;
  private rotation: number = 0;
  
  // 新增：自动旋转开关，默认为true
  private autoRotate: boolean = true;
  
  // 类实例保存的动画帧函数，确保可用，添加默认值避免初始化错误
  private requestAnimationFrame: (callback: FrameRequestCallback) => number = () => 0;
  private cancelAnimationFrame: (id: number) => void = () => {};
  
  // 新增：屏幕宽高信息，用于适配不同设备
  private screenWidth: number = 375; // 默认值
  private screenHeight: number = 667; // 默认值
  
  // 定义模型数据库类型
  private modelDatabase: {[key: string]: any} = {
    "68aeb52c5022af67203968ba": {
      _id: "68aeb52c5022af67203968ba",
      building_type: "weilong_house",
      name: "围龙屋",
      description: "围龙屋是粤东客家地区的传统民居，以中轴线为中心，左右对称，后有半月形围龙，象征团圆和谐，是客家文化的重要载体。",
      marker_features: {},
      model_url: "https://gitee.com/zzqqcom/resources/raw/master/static/models/old_town.glb",
      audio_urls: {
        mandarin: "https://gitee.com/zzqqcom/resources/raw/master/static/audio/test.mp3",
        hakka: "/static/audio/weilong_house_hakka.mp3"
      },
      created_at: "2024-05-20T08:00:00.000+00:00",
      updated_at: "2024-05-20T08:00:00.000+00:00"
    }
  };

  // 构造函数，接受已有的canvas和gl上下文
  constructor(canvas: any, gl: any) {
    this.canvas = canvas;
    this.gl = gl;
    
    // 新增：获取屏幕信息，用于适配手机端
    try {
      const systemInfo = wx.getSystemInfoSync();
      this.screenWidth = systemInfo.windowWidth;
      this.screenHeight = systemInfo.windowHeight;
    } catch (e) {
      console.warn('获取屏幕信息失败，使用默认值', e);
    }
    
    // 初始化小程序环境下的动画帧函数 - 确保在任何操作前完成
    this.initAnimationFramePolyfill();
    
    if (this.canvas && this.gl) {
      this.isInitialized = true;
      this.initCallbacks.forEach(callback => callback());
      this.initCallbacks = [];
    } else {
      this.initializeWebGL();
    }
  }

  /**
   * 初始化动画帧的polyfill，适配小程序环境
   * 主要修复：增强兼容性，直接挂载到全局对象
   */
  private initAnimationFramePolyfill() {
    // 小程序环境可能使用wx对象或其他全局对象
    const globalObj: (typeof globalThis | Wx) = typeof wx !== 'undefined' ? wx : globalThis;
    
    // 小程序环境下的requestAnimationFrame实现
    if (typeof globalObj.requestAnimationFrame === 'undefined') {
      // 存储动画帧ID的计数器
      let lastId = 0;
      const animationFrames: {[id: number]: FrameRequestCallback} = {};
      
      // 实现requestAnimationFrame
      (globalObj as any).requestAnimationFrame = (callback: FrameRequestCallback): number => {
        const id = ++lastId;
        animationFrames[id] = callback;
        
        // 使用小程序的nextTick或setTimeout实现
        // 修复：检查全局对象而不是直接检查wx，提高兼容性
        if (typeof (globalObj as Wx).nextTick === 'function') {
          (globalObj as Wx).nextTick(() => {
            if (animationFrames[id]) {
              animationFrames[id](Date.now());
              delete animationFrames[id];
            }
          });
        } else {
          setTimeout(() => {
            if (animationFrames[id]) {
              animationFrames[id](Date.now());
              delete animationFrames[id];
            }
          }, 16);
        }
        
        return id;
      };
      
      // 实现cancelAnimationFrame
      (globalObj as any).cancelAnimationFrame = (id: number): void => {
        delete animationFrames[id];
      };
    }
    
    // 将全局动画函数引用保存到类实例中，确保可以访问
    this.requestAnimationFrame = globalObj.requestAnimationFrame.bind(globalObj);
    this.cancelAnimationFrame = globalObj.cancelAnimationFrame.bind(globalObj);
    
    // TextDecoder polyfill
    if (typeof globalObj.TextDecoder === 'undefined') {
      (globalObj as any).TextDecoder = class {
        constructor(private encoding: string = 'utf-8') {}
        
        decode(buffer: ArrayBuffer): string {
          const uint8 = new Uint8Array(buffer);
          let str = '';
          for (let i = 0; i < uint8.length; i++) {
            str += String.fromCharCode(uint8[i]);
          }
          return str;
        }
      } as any;
    }
    
    // 添加URL polyfill，适配小程序环境
    if (typeof globalObj.URL === 'undefined') {
      (globalObj as any).URL = class {
        public href: string;
        
        constructor(url: string) {
          this.href = url;
        }
      } as any;
    }
  }

  /**
   * 设置模型缩放
   */
  setScale(scale: number) {
    this.scale = scale;
    // 修复：强制触发重新渲染
    this.forceRender();
  }
  
  /**
   * 设置模型旋转角度
   */
  setRotation(rotation: number) {
    this.rotation = rotation;
    // 修复：强制触发重新渲染
    this.forceRender();
  }

  /**
   * 新增：切换自动旋转
   */
  toggleAutoRotate(enable?: boolean) {
    this.autoRotate = enable !== undefined ? enable : !this.autoRotate;
    this.forceRender();
  }

  /**
   * 新增：强制触发一次渲染，确保参数变更立即生效
   */
  private forceRender() {
    if (this.animationFrameId !== null) {
      // 取消当前帧，立即触发一次新渲染
      this.cancelAnimationFrame(this.animationFrameId);
      // 立即手动渲染一帧
      if (this.gl && this.shaderProgram && this.vertexBuffer && this.indexBuffer) {
        this.renderFrame(Date.now());
      }
      // 继续渲染循环
      this.animationFrameId = this.requestAnimationFrame(this.renderFrame.bind(this));
    }
  }

  /**
   * 初始化WebGL上下文 - 适配微信小程序环境
   */
  private initializeWebGL() {
    if (!this.canvas) {
      const query = wx.createSelectorQuery();
      query.select('#arCanvas').node().exec((res: any) => {
        if (res && res[0] && res[0].node) {
          this.canvas = res[0].node;
          this.tryInitializeGL();
        } else {
          console.error('找不到canvas元素');
          wx.showToast({ title: '3D渲染环境初始化失败', icon: 'none' });
        }
      });
    } else {
      this.tryInitializeGL();
    }
  }

  /**
   * 尝试初始化GL上下文
   * 修复：添加上下文属性配置，支持透明背景
   */
  private tryInitializeGL() {
    try {
      if (!this.gl) {
        // 修复：手机端特别处理，确保创建透明背景的上下文
        const contextAttributes = {
          alpha: true, // 启用透明度
          premultipliedAlpha: false, // 禁用预乘alpha
          preserveDrawingBuffer: false,
          antialias: true,
          depth: true
        };
        
        this.gl = this.canvas.getContext('webgl', contextAttributes);
        if (!this.gl) {
          this.gl = this.canvas.getContext('experimental-webgl', contextAttributes);
        }
        
        if (!this.gl) {
          wx.showToast({ title: '当前设备不支持3D渲染', icon: 'none' });
          return;
        }
      }
      
      // 修复：设置canvas尺寸，适配手机屏幕
      const dpr = wx.getSystemInfoSync().pixelRatio;
      const width = this.screenWidth;
      const height = this.screenHeight;
      
      this.canvas.width = width * dpr;
      this.canvas.height = height * dpr;
      
      this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
      
      this.isInitialized = true;
      this.initCallbacks.forEach(callback => callback());
      this.initCallbacks = [];
    } catch (error) {
      console.error('WebGL初始化失败:', error);
      wx.showToast({ title: '3D渲染初始化失败', icon: 'none' });
    }
  }

  /**
   * 等待WebGL初始化完成
   */
  private waitForInitialization(): Promise<void> {
    return new Promise((resolve) => {
      if (this.isInitialized) {
        resolve();
      } else {
        this.initCallbacks.push(() => resolve());
      }
    });
  }

  /**
   * 根据模型ID获取数据库中的模型信息
   */
  private getModelInfo(modelId: string) {
    return this.modelDatabase[modelId];
  }

  /**
   * 加载并渲染3D模型
   * @param modelId 模型ID或URL（GLB格式）
   * @param initialScale 初始缩放比例
   * @param initialRotation 初始旋转角度
   */
  async loadAndRender(modelIdOrUrl: string, initialScale: number = 1.0, initialRotation: number = 0): Promise<void> {
    await this.waitForInitialization();
    
    if (!this.gl || !this.canvas) {
      wx.showToast({ title: '3D渲染环境未就绪', icon: 'none' });
      return;
    }
    
    // 设置初始变换参数
    this.scale = initialScale;
    this.rotation = initialRotation;

    try {
      wx.showLoading({ title: '加载3D模型中...' });

      // 1. 确定模型URL
      let modelUrl = modelIdOrUrl;
      const modelInfo = this.getModelInfo(modelIdOrUrl);
      if (modelInfo && modelInfo.model_url) {
        modelUrl = modelInfo.model_url;
        wx.showToast({ 
          title: `加载${modelInfo.name}...`, 
          icon: 'none',
          duration: 2000
        });
      }

      // 2. 下载模型文件
      const downloadRes = await this.downloadModel(modelUrl);
      
      // 检查响应状态码
      if (downloadRes.statusCode !== 200) {
        throw new Error(`模型下载失败，状态码: ${downloadRes.statusCode}`);
      }
      
      // 验证下载内容是否为二进制
      const arrayBuffer = downloadRes.data as ArrayBuffer;
      if (!arrayBuffer || arrayBuffer.byteLength < 12) {
        throw new Error('下载的模型文件为空或不完整');
      }
      
      // 初步检查GLB文件头
      const headerView = new DataView(arrayBuffer, 0, 12);
      const magic = headerView.getUint32(0, true);
      if (magic !== 0x46546C67) { // 'glTF'的ASCII码
        // 尝试查看前几个字符，帮助调试
        const textDecoder = new TextDecoder();
        const preview = textDecoder.decode(new Uint8Array(arrayBuffer, 0, Math.min(100, arrayBuffer.byteLength)));
        console.error('下载的文件不是有效的GLB格式，内容预览:', preview);
        
        // 提供备选模型URL，当主模型加载失败时使用
        const fallbackUrl = this.getFallbackModelUrl(modelUrl);
        if (fallbackUrl) {
          console.log(`尝试使用备选模型URL: ${fallbackUrl}`);
          return this.loadAndRender(fallbackUrl, initialScale, initialRotation);
        }
        
        throw new Error('下载的文件不是有效的GLB模型');
      }

      // 3. 解析GLB模型
      this.modelData = await this.parseGLB(arrayBuffer);

      if (!this.modelData || !this.modelData.vertices || !this.modelData.indices) {
        throw new Error('模型解析失败，无法获取顶点数据');
      }

      // 4. 初始化WebGL渲染环境
      this.initWebGL();
      
      // 5. 准备模型数据缓冲区
      this.prepareBuffers();
      
      // 6. 初始化着色器程序
      this.initShaderProgram();

      // 7. 开始渲染循环
      this.startRenderLoop();

      wx.hideLoading();
      wx.showToast({ 
        title: modelInfo ? `${modelInfo.name}加载完成` : '模型加载完成', 
        icon: 'success' 
      });

    } catch (error) {
      console.error('3D模型加载失败:', error);
      wx.hideLoading();
      wx.showToast({ title: (error as Error).message || '模型加载失败', icon: 'none' });
      throw error;
    }
  }

  /**
   * 为特定模型提供备选URL
   */
  private getFallbackModelUrl(originalUrl: string): string | null {
    // 围龙屋模型的备选URL
    if (originalUrl.includes('old_town.glb')) {
      return 'https://example.com/fallback-models/weilong_house.glb';
    }
    return null;
  }

  /**
   * 准备顶点和索引缓冲区
   */
  private prepareBuffers() {
    if (!this.gl || !this.modelData) return;
    
    const gl = this.gl;
    
    // 创建顶点缓冲区
    this.vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, this.modelData.vertices, gl.STATIC_DRAW);
    
    // 创建索引缓冲区
    this.indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.modelData.indices, gl.STATIC_DRAW);
    
    // 记录顶点数量
    this.vertexCount = this.modelData.indices.length;
  }

  /**
   * 停止动画渲染
   */
  stopRendering() {
    if (this.animationFrameId !== null) {
      // 使用类实例保存的取消动画帧方法，确保兼容性
      this.cancelAnimationFrame(this.animationFrameId);
      this.animationFrameId = null;
    }
  }

  /**
   * 下载模型文件
   */
  private async downloadModel(url: string): Promise<any> {
    return new Promise((resolve, reject) => {
      let fullUrl = url.startsWith('http') ? url : `http://192.168.134.31:5000${url}`;
      
      // 处理可能的URL编码问题
      try {
        fullUrl = new URL(fullUrl).href;
      } catch (e) {
        console.warn('URL格式可能不正确，尝试直接使用:', fullUrl);
      }
      
      console.log('开始下载模型:', fullUrl);
      
      wx.request({
        url: fullUrl,
        method: 'GET',
        responseType: 'arraybuffer',
        // 增加超时设置
        timeout: 30000, // 30秒超时
        success: resolve,
        fail: reject
      });
    });
  }

  /**
   * 解析GLB文件 - 增强JSON解析容错能力
   */
  private async parseGLB(arrayBuffer: ArrayBuffer): Promise<any> {
    try {
      const dataView = new DataView(arrayBuffer);
      
      // 1. 检查文件头 (12字节)
      const magic = dataView.getUint32(0, true);
      const version = dataView.getUint32(4, true);
      const fileLength = dataView.getUint32(8, true);
      
      // 检查是否是HTML内容（重定向或认证错误）
      const first100Bytes = new Uint8Array(arrayBuffer, 0, Math.min(100, arrayBuffer.byteLength));
      const textDecoder = new TextDecoder();
      const preview = textDecoder.decode(first100Bytes);
      
      if (preview.includes('<html') || preview.includes('<!DOCTYPE')) {
        throw new Error('下载的内容不是GLB文件，可能是HTML页面（重定向或认证错误）');
      }
      
      if (magic !== 0x46546C67) { // 'glTF'的ASCII码
        // 尝试查找可能的GLB文件头位置
        const possibleHeaderOffset = this.findPossibleGLBHeader(arrayBuffer);
        if (possibleHeaderOffset !== -1) {
          console.log(`在偏移量 ${possibleHeaderOffset} 处找到可能的GLB文件头，尝试从此处解析`);
          return this.parseGLBFromOffset(arrayBuffer, possibleHeaderOffset);
        }
        
        throw new Error('不是有效的GLB文件');
      }
      
      if (version !== 2) {
        throw new Error('仅支持GLB 2.0版本');
      }
      
      // 2. 解析块信息
      let offset = 12;
      
      if (offset + 12 > arrayBuffer.byteLength) {
        throw new Error('文件太小，无法读取JSON块头');
      }
      
      const jsonChunkHeader = new DataView(arrayBuffer, offset, 12);
      const jsonChunkLength = jsonChunkHeader.getUint32(0, true);
      const jsonChunkType = jsonChunkHeader.getUint32(4, true);
      
      if (jsonChunkType !== 0x4E4F534A) { // "JSON"
        // 尝试查找JSON块
        const possibleJsonOffset = this.findPossibleJSONChunk(arrayBuffer, offset);
        if (possibleJsonOffset !== -1) {
          console.log(`在偏移量 ${possibleJsonOffset} 处找到可能的JSON块，尝试从此处解析`);
          return this.parseGLBFromOffset(arrayBuffer, possibleJsonOffset - 12); // 减去块头大小
        }
        
        throw new Error('第一个块必须是JSON块');
      }
      
      offset += 12;
      
      // 确保不会超出缓冲区范围
      const maxOffset = Math.min(offset + jsonChunkLength, arrayBuffer.byteLength);
      const actualJsonLength = maxOffset - offset;
      
      if (actualJsonLength < jsonChunkLength) {
        console.warn(`JSON块长度超出文件范围: 预期 ${jsonChunkLength} 字节, 实际 ${actualJsonLength} 字节`);
      }
      
      // 3. 解析JSON内容
      const jsonChunk = new Uint8Array(arrayBuffer, offset, actualJsonLength);
      
      try {
        // 尝试多种编码解码
        let jsonText = '';
        const encodings = ['utf-8', 'latin1', 'utf-16'];
        
        for (const encoding of encodings) {
          try {
            jsonText = new TextDecoder(encoding).decode(jsonChunk);
            if (jsonText.length > 0) {
              console.log(`使用${encoding}编码成功解码JSON块`);
              break;
            }
          } catch (e) {
            console.log(`使用${encoding}编码解码失败:`, e);
          }
        }
        
        if (!jsonText) {
          throw new Error('无法解码JSON块内容');
        }
        
        // 改进的JSON修复逻辑
        let fixedJsonText = jsonText.trim();
        
        // 查找JSON的起始和结束位置
        const startIndex = fixedJsonText.indexOf('{');
        const endIndex = fixedJsonText.lastIndexOf('}');
        
        if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
          // 提取完整的JSON部分
          fixedJsonText = fixedJsonText.substring(startIndex, endIndex + 1);
          console.log('提取JSON核心部分成功');
        } else if (startIndex !== -1) {
          // 只有开始没有结束，尝试添加
          fixedJsonText = fixedJsonText.substring(startIndex) + '}';
          console.log('修复JSON结束符');
        } else {
          // 尝试在整个文件中查找JSON结构
          const jsonStructure = this.findJsonStructureInBinary(jsonChunk);
          if (jsonStructure) {
            fixedJsonText = jsonStructure;
            console.log('从二进制数据中提取JSON结构成功');
          } else {
            throw new Error('JSON内容格式不正确，未找到有效的JSON结构');
          }
        }
        
        // 多次尝试修复直到JSON有效或达到最大尝试次数
        let attempt = 0;
        const maxAttempts = 5;
        while (!this.isValidJson(fixedJsonText) && attempt < maxAttempts) {
          console.log(`尝试修复JSON (第${attempt + 1}次)`);
          fixedJsonText = this.tryFixJson(fixedJsonText);
          attempt++;
        }
        
        if (!this.isValidJson(fixedJsonText)) {
          // 最后的尝试：使用更严格的修复策略
          fixedJsonText = this.aggressiveJsonFix(fixedJsonText);
          if (!this.isValidJson(fixedJsonText)) {
            // 如果仍然无效，尝试使用备用的简单几何体
            console.warn('JSON格式修复失败，使用备用几何体');
            return this.createFallbackGeometry();
          }
        }
        
        const gltf = JSON.parse(fixedJsonText);
        offset += jsonChunkLength;
        
        // 4. 解析二进制块
        let binaryChunk: Uint8Array | null = null;
        if (offset < arrayBuffer.byteLength) {
          if (offset + 12 > arrayBuffer.byteLength) {
            console.warn('文件长度不足，无法读取二进制块头，跳过二进制块解析');
          } else {
            const binaryChunkHeader = new DataView(arrayBuffer, offset, 12);
            const binaryChunkLength = binaryChunkHeader.getUint32(0, true);
            const binaryChunkType = binaryChunkHeader.getUint32(4, true);
            
            if (binaryChunkType === 0x004E4942) { // "BIN"
              offset += 12;
              const binaryEnd = Math.min(offset + binaryChunkLength, arrayBuffer.byteLength);
              binaryChunk = new Uint8Array(arrayBuffer, offset, binaryEnd - offset);
            } else {
              console.warn('第二个块不是二进制块，跳过');
            }
          }
        }
        
        // 5. 提取顶点和索引数据
        return this.extractGeometryData(gltf, binaryChunk);
      } catch (decodeError) {
        console.error('JSON块解码失败:', decodeError);
        
        // 提取部分数据进行预览
        const previewLength = Math.min(200, jsonChunk.length);
        const previewChunk = new Uint8Array(jsonChunk.buffer, jsonChunk.byteOffset, previewLength);
        const previewText = new TextDecoder().decode(previewChunk);
        console.error('JSON块内容预览:', previewText);
        
        // 尝试使用备用的简单几何体
        console.warn('JSON解析失败，使用备用几何体');
        return this.createFallbackGeometry();
      }
      
    } catch (error) {
      console.error('GLB解析错误:', error);
      
      // 尝试使用备用的简单几何体
      console.warn('GLB解析失败，使用备用几何体');
      return this.createFallbackGeometry();
    }
  }

  /**
   * 查找可能的GLB文件头位置
   */
  private findPossibleGLBHeader(arrayBuffer: ArrayBuffer): number {
    const view = new Uint8Array(arrayBuffer);
    const magic = [0x67, 0x6C, 0x54, 0x46]; // 'glTF'的字节表示
    
    // 在文件中查找可能的GLB文件头
    for (let i = 0; i < view.length - 4; i++) {
      if (view[i] === magic[0] && 
          view[i + 1] === magic[1] && 
          view[i + 2] === magic[2] && 
          view[i + 3] === magic[3]) {
        return i;
      }
    }
    
    return -1;
  }

  /**
   * 查找可能的JSON块
   */
  private findPossibleJSONChunk(arrayBuffer: ArrayBuffer, startOffset: number): number {
    const view = new Uint8Array(arrayBuffer);
    const jsonMagic = [0x4E, 0x4F, 0x53, 0x4A]; // "JSON"的字节表示
    
    // 在文件中查找可能的JSON块
    for (let i = startOffset; i < view.length - 4; i++) {
      if (view[i] === jsonMagic[0] && 
          view[i + 1] === jsonMagic[1] && 
          view[i + 2] === jsonMagic[2] && 
          view[i + 3] === jsonMagic[3]) {
        return i;
      }
    }
    
    return -1;
  }

  /**
   * 从指定偏移量开始解析GLB文件
   */
  private parseGLBFromOffset(arrayBuffer: ArrayBuffer, offset: number): Promise<any> {
    // 创建一个新的ArrayBuffer，从指定偏移量开始
    const newBuffer = arrayBuffer.slice(offset);
    return this.parseGLB(newBuffer);
  }

  /**
   * 在二进制数据中查找JSON结构
   */
  private findJsonStructureInBinary(data: Uint8Array): string | null {
    const textDecoder = new TextDecoder();
    const text = textDecoder.decode(data);
    
    // 查找可能的JSON结构
    const jsonStart = text.indexOf('{');
    const jsonEnd = text.lastIndexOf('}');
    
    if (jsonStart !== -1 && jsonEnd !== -1 && jsonEnd > jsonStart) {
      return text.substring(jsonStart, jsonEnd + 1);
    }
    
    return null;
  }

  /**
   * 创建备用几何体（当GLB解析失败时使用）
   * 修复：进一步缩小模型尺寸，使其在真机上显示合适
   */
  private createFallbackGeometry(): any {
    console.log('创建备用几何体 - 蓝色半透明房子模型');
    
    // 房子由两部分组成：底部正方体（主体）和顶部三角形（屋顶）
    // 顶点坐标：x, y, z
    // 修复：将原尺寸缩小为1/6，使其更小，避免遮挡按钮和相机视图
    
    // 底部正方体顶点 (y从-0.15到0.15) - 尺寸缩小为原来的1/6
    const cubeVertices = [
      // 前面
      -0.15, -0.15, 0.15,  0.15, -0.15, 0.15,  0.15, 0.15, 0.15, -0.15, 0.15, 0.15,
      // 后面
      -0.15, -0.15, -0.15, -0.15, 0.15, -0.15, 0.15, 0.15, -0.15, 0.15, -0.15, -0.15,
      // 上面
      -0.15, 0.15, -0.15, -0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, -0.15,
      // 下面
      -0.15, -0.15, -0.15, 0.15, -0.15, -0.15, 0.15, -0.15, 0.15, -0.15, -0.15, 0.15,
      // 右面
      0.15, -0.15, -0.15, 0.15, 0.15, -0.15, 0.15, 0.15, 0.15, 0.15, -0.15, 0.15,
      // 左面
      -0.15, -0.15, -0.15, -0.15, -0.15, 0.15, -0.15, 0.15, 0.15, -0.15, 0.15, -0.15
    ];
    
    // 顶部三角形屋顶顶点 (y从0.15到0.45) - 相应缩小尺寸
    const roofVertices = [
      // 前面三角形
      -0.18, 0.15, 0.15,  0.18, 0.15, 0.15,  0, 0.45, 0.15,
      // 后面三角形
      -0.18, 0.15, -0.15, 0.18, 0.15, -0.15, 0, 0.45, -0.15,
      // 左侧三角形
      -0.18, 0.15, -0.15, -0.18, 0.15, 0.15, 0, 0.45, 0,
      // 右侧三角形
      0.18, 0.15, -0.15, 0.18, 0.15, 0.15, 0, 0.45, 0
    ];
    
    // 合并所有顶点
    const vertices = new Float32Array([...cubeVertices, ...roofVertices]);
    
    // 正方体索引 (0-23)
    const cubeIndices = [
      0, 1, 2, 0, 2, 3,     // 前面
      4, 5, 6, 4, 6, 7,     // 后面
      8, 9, 10, 8, 10, 11,  // 上面
      12, 13, 14, 12, 14, 15, // 下面
      16, 17, 18, 16, 18, 19, // 右面
      20, 21, 22, 20, 22, 23  // 左面
    ];
    
    // 屋顶索引 (24-47，基于前面的24个正方体顶点)
    const roofIndices = [
      24, 25, 26,           // 前面三角形
      27, 28, 29,           // 后面三角形
      30, 31, 32,           // 左侧三角形
      33, 34, 35            // 右侧三角形
    ];
    
    // 合并所有索引
    const indices = new Uint16Array([...cubeIndices, ...roofIndices]);
    
    return {
      vertices,
      indices,
      vertexCount: indices.length
    };
  }

  /**
   * 添加更激进的JSON修复方法
   */
  private aggressiveJsonFix(jsonText: string): string {
    let fixed = jsonText;
    
    // 移除注释
    fixed = fixed.replace(/\/\/.*$/gm, '');
    fixed = fixed.replace(/\/\*[\s\S]*?\*\//g, '');
    
    // 修复引号问题
    fixed = fixed.replace(/(['"])(.*?)\1/g, (match, quote, content) => {
      // 处理内容中的引号
      const cleanedContent = content.replace(/["']/g, '');
      return `${quote}${cleanedContent}${quote}`;
    });
    
    // 修复属性名缺少引号的问题
    fixed = fixed.replace(/(\w+)\s*:/g, '"$1":');
    
    // 修复数组和对象末尾的逗号
    fixed = fixed.replace(/,\s*([\]}])/g, '$1');
    
    // 平衡括号
    let braceBalance = 0;
    let bracketBalance = 0;
    for (const char of fixed) {
      if (char === '{') braceBalance++;
      else if (char === '}') braceBalance--;
      else if (char === '[') bracketBalance++;
      else if (char === ']') bracketBalance--;
    }
    
    while (braceBalance > 0) {
      fixed += '}';
      braceBalance--;
    }
    
    while (bracketBalance > 0) {
      fixed += ']';
      bracketBalance--;
    }
    
    return fixed;
  }
  
  /**
   * 简单检查JSON是否有效
   */
  private isValidJson(jsonText: string): boolean {
    try {
      JSON.parse(jsonText);
      return true;
    } catch {
      return false;
    }
  }
  
  /**
   * 尝试修复简单的JSON格式问题
   */
  private tryFixJson(jsonText: string): string {
    let fixed = jsonText;
    
    // 计算括号平衡
    let braceBalance = 0;
    for (const char of fixed) {
      if (char === '{') braceBalance++;
      else if (char === '}') braceBalance--;
    }
    
    // 修复括号不平衡问题
    while (braceBalance > 0) {
      fixed += '}';
      braceBalance--;
    }
    
    // 尝试修复其他常见问题
    fixed = fixed.replace(/,\s*([\]}])/g, '$1'); // 移除尾随逗号
    
    return fixed;
  }

  /**
   * 从glTF数据中提取几何体数据
   */
  private extractGeometryData(gltf: any, binaryChunk: Uint8Array | null): any {
    try {
      // 查找第一个网格
      if (!gltf.meshes || gltf.meshes.length === 0) {
        throw new Error('模型中没有找到网格数据');
      }
      
      // 获取第一个图元
      const mesh = gltf.meshes[0];
      const primitive = mesh.primitives[0];
      
      // 获取属性访问器
      const positionAccessor = gltf.accessors[primitive.attributes.POSITION];
      const indexAccessor = gltf.accessors[primitive.indices];
      
      // 获取缓冲视图
      const positionBufferView = gltf.bufferViews[positionAccessor.bufferView];
      const indexBufferView = gltf.bufferViews[indexAccessor.bufferView];
      
      // 获取缓冲区
      const buffer = binaryChunk || (gltf.buffers && gltf.buffers[0] ? new Uint8Array(gltf.buffers[0].uri) : null);
      
      if (!buffer) {
        throw new Error('未找到模型二进制数据');
      }
      
      // 提取顶点数据
      const vertices = this.extractVertices(buffer, positionBufferView, positionAccessor);
      
      // 提取索引数据
      const indices = this.extractIndices(buffer, indexBufferView, indexAccessor);
      
      return {
        vertices,
        indices,
        vertexCount: indices.length
      };
    } catch (error) {
      console.error('提取几何体数据失败:', error);
      throw error;
    }
  }

  /**
   * 提取顶点数据
   */
  private extractVertices(buffer: Uint8Array, bufferView: any, accessor: any): Float32Array {
    const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
    const componentType = accessor.componentType;
    const itemSize = accessor.type === 'VEC3' ? 3 : 2; // 假设是2D或3D顶点
    const count = accessor.count;
    
    // 检查偏移是否有效
    if (byteOffset < 0 || byteOffset + count * itemSize * this.getComponentSize(componentType) > buffer.byteLength) {
      throw new Error(`顶点数据偏移超出缓冲区范围: ${byteOffset}`);
    }
    
    // 根据组件类型创建相应的视图
    let dataView;
    switch (componentType) {
      case 5126: // FLOAT
        dataView = new Float32Array(
          buffer.buffer, 
          byteOffset, 
          count * itemSize
        );
        break;
      case 5123: // UNSIGNED_SHORT
        const usArray = new Uint16Array(
          buffer.buffer, 
          byteOffset, 
          count * itemSize
        );
        dataView = new Float32Array(usArray.length);
        for (let i = 0; i < usArray.length; i++) {
          dataView[i] = usArray[i];
        }
        break;
      case 5125: // UNSIGNED_INT
        const uiArray = new Uint32Array(
          buffer.buffer, 
          byteOffset, 
          count * itemSize
        );
        dataView = new Float32Array(uiArray.length);
        for (let i = 0; i < uiArray.length; i++) {
          dataView[i] = uiArray[i];
        }
        break;
      default:
        throw new Error(`不支持的顶点组件类型: ${componentType}`);
    }
    
    return dataView;
  }
  
  /**
   * 获取组件类型的大小（字节）
   */
  private getComponentSize(componentType: number): number {
    switch (componentType) {
      case 5120: return 1; // BYTE
      case 5121: return 1; // UNSIGNED_BYTE
      case 5122: return 2; // SHORT
      case 5123: return 2; // UNSIGNED_SHORT
      case 5125: return 4; // UNSIGNED_INT
      case 5126: return 4; // FLOAT
      default: return 4;
    }
  }

  /**
   * 提取索引数据
   */
  private extractIndices(buffer: Uint8Array, bufferView: any, accessor: any): Uint16Array | Uint32Array {
    const byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0);
    const componentType = accessor.componentType;
    const count = accessor.count;
    
    // 检查偏移是否有效
    if (byteOffset < 0 || byteOffset + count * this.getComponentSize(componentType) > buffer.byteLength) {
      throw new Error(`索引数据偏移超出缓冲区范围: ${byteOffset}`);
    }
    
    switch (componentType) {
      case 5123: // UNSIGNED_SHORT
        return new Uint16Array(
          buffer.buffer, 
          byteOffset, 
          count
        );
      case 5125: // UNSIGNED_INT
        return new Uint32Array(
          buffer.buffer, 
          byteOffset, 
          count
        );
      default:
        throw new Error(`不支持的索引组件类型: ${componentType}`);
    }
  }

  /**
   * 初始化WebGL环境
   * 主要修复：启用混合模式以支持透明度，确保手机端透明背景
   */
  private initWebGL() {
    if (!this.gl) return;
    const gl = this.gl;

    // 设置完全透明的清除颜色
    gl.clearColor(0, 0, 0, 0);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.CULL_FACE);
    // 启用混合模式以支持透明度
    gl.enable(gl.BLEND);
    // 设置混合函数：源颜色乘以源alpha，目标颜色乘以(1-源alpha)
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  }

  /**
   * 初始化着色器程序
   */
  private initShaderProgram() {
    if (!this.gl) return;
    
    const gl = this.gl;
    
    // 顶点着色器
    const vertexShaderSource = `
      attribute vec3 aPosition;
      uniform mat4 uModelViewMatrix;
      uniform mat4 uProjectionMatrix;
      
      void main() {
        gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
      }
    `;
    
    // 片元着色器
    // 主要修改：将颜色改为蓝色，并支持透明度控制
    const fragmentShaderSource = `
      precision mediump float;
      uniform vec4 uColor;  // 修改为vec4以支持alpha通道
      
      void main() {
        gl_FragColor = uColor;  // 使用包含alpha的颜色
      }
    `;
    
    // 编译着色器
    const vertexShader = this.compileShader(gl.VERTEX_SHADER, vertexShaderSource);
    const fragmentShader = this.compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
    
    // 创建着色器程序
    this.shaderProgram = gl.createProgram();
    if (!this.shaderProgram) return;
    
    gl.attachShader(this.shaderProgram, vertexShader);
    gl.attachShader(this.shaderProgram, fragmentShader);
    gl.linkProgram(this.shaderProgram);
    
    if (!gl.getProgramParameter(this.shaderProgram, gl.LINK_STATUS)) {
      console.error('着色器程序链接失败:', gl.getProgramInfoLog(this.shaderProgram));
      return;
    }
    
    gl.useProgram(this.shaderProgram);
  }

  /**
   * 编译着色器
   */
  private compileShader(type: number, source: string): WebGLShader | null {
    if (!this.gl) return null;
    
    const gl = this.gl;
    const shader = gl.createShader(type);
    if (!shader) return null;
    
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
      console.error('着色器编译失败:', gl.getShaderInfoLog(shader));
      gl.deleteShader(shader);
      return null;
    }
    
    return shader;
  }

  /**
   * 开始渲染循环
   * 主要修复：使用类实例保存的动画帧函数，确保调用有效
   */
  private startRenderLoop() {
    if (!this.gl || !this.shaderProgram || !this.vertexBuffer || !this.indexBuffer) return;
    
    // 额外检查动画帧函数是否可用
    if (typeof this.requestAnimationFrame !== 'function') {
      console.error('requestAnimationFrame 函数不可用，无法启动渲染循环');
      // 尝试再次初始化polyfill
      this.initAnimationFramePolyfill();
      
      // 如果仍然不可用，显示错误信息
      if (typeof this.requestAnimationFrame !== 'function') {
        wx.showToast({ title: '无法启动3D渲染', icon: 'none' });
        return;
      }
    }
    
    // 停止任何现有的渲染循环
    this.stopRendering();
    
    // 开始新的渲染循环
    this.animationFrameId = this.requestAnimationFrame(this.renderFrame.bind(this));
  }

  /**
   * 新增：单独的渲染帧函数，使forceRender可以直接调用
   * 修复：确保手机端透明背景
   */
  private renderFrame(timestamp: number) {
    if (!this.gl || !this.shaderProgram || !this.vertexBuffer || !this.indexBuffer) return;
    
    const gl = this.gl;
    
    // 设置完全透明的清除颜色，确保不遮挡相机
    gl.clearColor(0.0, 0.0, 0.0, 0.0);
    gl.enable(gl.DEPTH_TEST);
    
    // 获取属性和 uniform 位置
    const positionAttributeLocation = gl.getAttribLocation(this.shaderProgram, 'aPosition');
    const projectionMatrixLocation = gl.getUniformLocation(this.shaderProgram, 'uProjectionMatrix');
    const modelViewMatrixLocation = gl.getUniformLocation(this.shaderProgram, 'uModelViewMatrix');
    // 修改：获取vec4颜色的位置
    const colorLocation = gl.getUniformLocation(this.shaderProgram, 'uColor');
    
    // 启用顶点属性
    gl.enableVertexAttribArray(positionAttributeLocation);
    gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
    gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
    
    // 设置投影矩阵
    const projectionMatrix = this.createProjectionMatrix();
    
    // 清除画布 - 使用完全透明
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
    // 更新模型视图矩阵
    const modelViewMatrix = this.createModelViewMatrix(timestamp);
    
    // 设置 uniform
    gl.uniformMatrix4fv(projectionMatrixLocation, false, projectionMatrix);
    gl.uniformMatrix4fv(modelViewMatrixLocation, false, modelViewMatrix);
    // 主要修改：将颜色改为蓝色，并设置透明度为0.7
    gl.uniform4f(colorLocation, 0.2, 0.4, 0.8, 0.7); // 蓝色半透明
    
    // 绘制模型
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
    gl.drawElements(gl.TRIANGLES, this.vertexCount, gl.UNSIGNED_SHORT, 0);
    
    // 继续渲染循环
    this.animationFrameId = this.requestAnimationFrame(this.renderFrame.bind(this));
  }

  /**
   * 创建投影矩阵
   * 修复：适配手机屏幕比例，减小视场角
   */
  private createProjectionMatrix(): Float32Array {
    // 根据屏幕宽高比调整视场角
    const aspect = this.screenWidth / this.screenHeight;
    const fieldOfView = 25 * Math.PI / 180; // 减小视场角，使模型看起来更小
    const zNear = 0.1;
    const zFar = 100.0; // 减小远平面距离
    const matrix = new Float32Array(16);
    
    // 透视投影矩阵
    const f = 1.0 / Math.tan(fieldOfView / 2);
    matrix[0] = f / aspect;
    matrix[5] = f;
    matrix[10] = (zFar + zNear) / (zNear - zFar);
    matrix[11] = -1;
    matrix[14] = (2 * zFar * zNear) / (zNear - zFar);
    matrix[15] = 0;
    
    return matrix;
  }

  /**
   * 创建模型视图矩阵
   * 主要修复：适配手机端，调整模型位置和大小
   */
  private createModelViewMatrix(timestamp: number): Float32Array {
    const matrix = new Float32Array(16);
    
    // 单位矩阵
    matrix[0] = 1;
    matrix[5] = 1;
    matrix[10] = 1;
    matrix[15] = 1;
    
    // 平移 - 根据屏幕尺寸调整模型位置
    const isMobile = this.screenWidth < 768; // 判断是否为手机端
    const zPosition = isMobile ? -3.5 : -5.0; // 手机端模型更靠近相机
    this.translateMatrix(matrix, 0, 0, zPosition);
    
    // 缩放 - 应用用户设置的缩放，手机端默认缩小
    const mobileScale = isMobile ? 0.8 : 1.0;
    this.scaleMatrix(matrix, this.scale * mobileScale);
    
    // 旋转 - 分离自动旋转和用户设置的旋转
    let totalRotation = this.rotation * Math.PI / 180; // 将用户输入的角度转换为弧度
    
    // 如果启用自动旋转，则添加时间相关的旋转
    if (this.autoRotate) {
      totalRotation += timestamp * 0.0005;
    }
    
    this.rotateYMatrix(matrix, totalRotation);
    
    return matrix;
  }

  /**
   * 矩阵平移变换
   */
  private translateMatrix(matrix: Float32Array, x: number, y: number, z: number) {
    // 右乘平移矩阵
    const result = new Float32Array(matrix);
    matrix[12] = result[0] * x + result[4] * y + result[8] * z + result[12];
    matrix[13] = result[1] * x + result[5] * y + result[9] * z + result[13];
    matrix[14] = result[2] * x + result[6] * y + result[10] * z + result[14];
    matrix[15] = result[3] * x + result[7] * y + result[11] * z + result[15];
  }

  /**
   * 矩阵缩放变换
   */
  private scaleMatrix(matrix: Float32Array, scale: number) {
    for (let i = 0; i < 16; i++) {
      if (i === 0 || i === 5 || i === 10) {
        matrix[i] *= scale;
      }
    }
  }

  /**
   * 矩阵Y轴旋转变换
   */
  private rotateYMatrix(matrix: Float32Array, angle: number) {
    const cos = Math.cos(angle);
    const sin = Math.sin(angle);
    
    // 右乘旋转矩阵
    const result = new Float32Array(matrix);
    matrix[0] = result[0] * cos + result[8] * sin;
    matrix[1] = result[1] * cos + result[9] * sin;
    matrix[2] = result[2] * cos + result[10] * sin;
    matrix[3] = result[3] * cos + result[11] * sin;
    matrix[8] = result[0] * (-sin) + result[8] * cos;
    matrix[9] = result[1] * (-sin) + result[9] * cos;
    matrix[10] = result[2] * (-sin) + result[10] * cos;
    matrix[11] = result[3] * (-sin) + result[11] * cos;
  }
}
